﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

using Furion.AspNetCore.Extensions;
using Furion.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Hosting;
using Neto.Application;
using Neto.Core.CoreServices;
using System;
using System.Collections.Generic;

namespace Neto.Web.Core;

[AppStartup(700)]
public class Startup : AppStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //services.AddFileLogging(options =>
        //{
        //    //options.MessageFormat = LoggerFormatter.JsonIndented;
        //    //启用备用日志文件功能，也就是如果文件被占用了，可以创建新的备用日志继续写入，推荐！！！
        //    options.HandleWriteError = (writeError) =>
        //    {
        //        writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName));
        //    };
        //});
        //添加ES日志扩展
        //services.AddEsLog();

        #region 分布式缓存 SqlServer,其它方式后面在加

        //注册分布式数据库缓存
        //services.AddDistributedSqlServerCache(options =>
        //{
        //    options.ConnectionString = Configuration.GetConnectionString("DbCacheConnectionString");
        //    options.SchemaName = "dbo";
        //    options.TableName = "sys_DataCache";
        //    options.SystemClock = new SystemClockEx();
        //});

        #endregion 分布式缓存 SqlServer,其它方式后面在加

        #region ActionFilter 授权验证

        // services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        //     //后台
        //     .AddCookie(AuthorizeType.AdminAuthenticationScheme, options =>
        //     {
        //         //options.AccessDeniedPath = "/Account/Forbidden";
        //         options.LoginPath = "/HtAdmin/Login/Index";
        //         options.LogoutPath = "/HtAdmin/Login/LoginOut";
        //     });

        #endregion ActionFilter 授权验证

        #region 自定义权限策略

        //services.AddAuthorization(options =>
        //{
        //    options.AddPolicy("AbcPolicy", policy =>
        //    {
        //        policy.Requirements.Add(new AbcAuthorizationRequirement());
        //    });

        //});
        //services.AddSingleton<IAuthorizationHandler, AbcAuthorizationHandler>();

        #endregion 自定义权限策略

        // 默认授权机制，需授权的即可（方法）需贴 `[Authorize]` 特性
        //services.AddJwt<JwtHandler>();
        services.AddJwt<JwtHandler>(enableGlobalAuthorize: true); //必须在 .AddControllers() 之前注册！！

        services.AddCorsAccessor(); //添加跨域配置
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromDays(1); // TimeSpan.FromMinutes(30);
        }); //Session

        services.Configure<RazorViewEngineOptions>(options =>
        {
            //自定视图文件，添加视图子文件夹的情况下很有用
            // 区域
            // 添加自定义视图路径方法
            // Areas/HtAdmin/Views/ERL/Article/Index.cshtml
            // Areas/区域名称/Views/文件夹名称/控制器Controller/方法Action.cshtml
            // Areas/{2}/Views/ERL/{1}/{0}.cshtml

            var types = new List<string>
            {
                "Defaults", //默认
                "Devs", //开发的
                "Syss", //系统设置
                "User", //管理
                "Email", //邮件
                "SMS", //短信
                "Logs", //系统日志
                "Statistics", //统计日志
                "Others" //其它
            };
            foreach (var type in types)
                options.AreaViewLocationFormats.Add($"~/Areas/{{2}}/Views/{type}/{{1}}/{{0}}" +
                                                    RazorViewEngine.ViewExtension);

            //options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Defaults/{1}/{0}" + RazorViewEngine.ViewExtension);
            //options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Devs/{1}/{0}" + RazorViewEngine.ViewExtension);
            //options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/Syss/{1}/{0}" + RazorViewEngine.ViewExtension);
            //options.AreaViewLocationFormats.Add("~/Areas/{2}/Views/User/{1}/{0}" + RazorViewEngine.ViewExtension);
            // 非区域 添加自定义视图路径方法 /Views/ERL/Article/Index.cshtml
            // /Views/文件夹名称/控制器Controller/方法Action.cshtml /Views/ERL/{1}/{0}.cshtml options.ViewLocationFormats.Add("~/Views/ERL/{0}"+RazorViewEngine.ViewExtension);
        });

        services.AddRazorPages();

        //滑块验证码
        services.AddDistributedMemoryCache();
        services.AddSlideCaptcha(App.Configuration);

        //图形验证码
        services.AddCaptcha(App.Configuration);

        //, option =>
        //  {
        //      option.CaptchaType = CaptchaType.WORD; // 验证码类型
        //      option.CodeLength = 4; // 验证码长度, 要放在CaptchaType设置后.  当类型为算术表达式时，长度代表操作的个数
        //      option.ExpirySeconds = 30; // 验证码过期时间
        //      option.IgnoreCase = true; // 比较时是否忽略大小写
        //      option.StoreageKeyPrefix = ""; // 存储键前缀

        //      option.ImageOption.Animation = true; // 是否启用动画
        //      option.ImageOption.FrameDelay = 30; // 每帧延迟,Animation=true时有效, 默认30

        //      option.ImageOption.Width = 150; // 验证码宽度
        //      option.ImageOption.Height = 50; // 验证码高度
        //      option.ImageOption.BackgroundColor = SkiaSharp.SKColor.Parse("#fff"); // 验证码背景色

        //      option.ImageOption.BubbleCount = 2; // 气泡数量
        //      option.ImageOption.BubbleMinRadius = 5; // 气泡最小半径
        //      option.ImageOption.BubbleMaxRadius = 15; // 气泡最大半径
        //      option.ImageOption.BubbleThickness = 1; // 气泡边沿厚度

        //      option.ImageOption.InterferenceLineCount = 2; // 干扰线数量

        //      option.ImageOption.FontSize = 36; // 字体大小
        //      option.ImageOption.FontFamily = DefaultFontFamilys.Instance.Actionj; // 字体

        //      /*
        //       * 中文使用kaiti，其他字符可根据喜好设置（可能部分转字符会出现绘制不出的情况）。
        //       * 当验证码类型为“ARITHMETIC”时，不要使用“Ransom”字体。（运算符和等号绘制不出来）
        //       */

        //      option.ImageOption.TextBold = true;// 粗体，该配置2.0.3新增
        //  }

        ////新增全球化本地化Json扩展
        //services.AddJsonLocalization(options =>
        //{
        //    options.ResourcesPath = "Resources";
        //    options.ResourcesPathType = ResourcesPathType.从数据库缓存读取;
        //    //options.CacheKeyPrefix = appInfoOption.Localization_CacheKeyPrefix; //"Localization_CacheKeyPrefix_"; //缓存Key (格式：固定_语种的culture,英语：Localization_CacheKeyPrefix_en-US)
        //});

        //services.TryAddSingleton<IStringLocalizerFactory, StringXLocalizerFactory>();


        services.AddControllersWithViews(options =>
            {
                //options.Filters.Add(new ErrorPageFilter("HtAdmin"));
                //Web 拦截器
                options.Filters.Add(new WebAccessLogFilter());//全局action访问日志，默认不打开
                ////此处注册全局 输入验证
                //options.Filters.Add(new ValidateEntityFilterAttribute(ValidationEntityType.All, isMvcReutrnMultipleErrors: true));
                //此处 注册全局   防止重复提交 特性(影响性能,有1~10毫秒左右影响)
                //options.Filters.Add(new PreventSpamFilterAttribute(httpAccessInterval: 1000));
                options.Filters.Add(new XSSFilterAttribute()); //xss攻击防御
            })
            .AddAbcJsonOptions(true, true) //system.text.json
            //.AddAbcNewtonsoftJson()//newtonsoft.json 这两个使用一个就好了
            .AddLocalizationX() // 注册多语言
            .AddInjectWithUnifyResult(options => { });


        // 添加敏感词服务
        services.AddSingleton<SensitiveWordService>();

  

        //设置文件上传限制，默认为 128M
        services.Configure<FormOptions>(options =>
        {
            options.ValueCountLimit = 128 * 1024 * 1024; //默认128M
            options.MultipartBodyLengthLimit = 128 * 1024 * 1024; //默认128M
        });

        //对于获取客户端 IP 的场景只需要指定 ForwardedHeaders.XForwardedFor
        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            //清除已知网络和已知代理的默认列表
            options.KnownNetworks.Clear();
            options.KnownProxies.Clear();
        });
        // 添加实时通讯
        services.AddSignalR();

        //防止跨站点请请求伪造xsrf/csrf
        //services.AddCSRF();

        services.AddViewEngine(); //注册视图引擎
        //注册 监听日志
        //services.AddMvcFilter<LoggingMonitorAttribute>();
        services.AddMonitorLogging();


        #region 文件上传配置

        services.Configure<IISServerOptions>(options =>
        {
            options.MaxRequestBodySize = null;
        });
        //所有controller都不限制post的body大小
        services.Configure<KestrelServerOptions>(options =>
        {
            options.Limits.MaxRequestBodySize = null;
        });
        // Multipart body length limit 134217728 exceeded错误
        //Post的body大概超过128多M会碰到这个错误
        //完整的错误信息是 System.IO.InvalidDataException: Multipart body length limit 134217728 exceeded.
        services.Configure<FormOptions>(options =>
        {
            options.ValueLengthLimit = int.MaxValue;
            options.MultipartBodyLengthLimit = int.MaxValue; //In case of multipart
        });

        #endregion
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        //如果环境是Development serve Developer Exception Page
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
        //否则显示用户友好的错误页面
        else if (env.IsStaging() || env.IsProduction() || env.IsEnvironment("UAT"))
            //else
        {
            app.UseExceptionHandler("/Error");
            //拦截404页面
            app.UseStatusCodePagesWithReExecute("/Error/NotFound/{0}");
        }

        if (env.IsDevelopment())
            //添加 Forwarded Headers 中间件
            app.UseForwardedHeaders();
        else
        {
            //添加 Forwarded Headers 中间件
            app.UseForwardedHeaders();
            app.UseHsts();
        }

        //删除特定的 HTTP 响应头
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Remove("furion");
            context.Response.Headers.Remove("environment");

            await next();
        });

        // 添加规范化结果状态码，需要在这里注册
        // app.UseUnifyResultStatusCodes();
        // 配置多语言，必须在 路由注册之前
        app.UseAppLocalization();

        app.UseHttpsRedirection();
        app.UseCustomStaticFile();
        app.UseStaticFiles();
        //默认开发可用。。。
        if (env.IsDevelopment()) app.UseScheduleUI(); //Schedule 定时任务看板

        app.UseEnableBuffering(); //EnableBuffering()// //需要放UseRouting前面
        //app.UseSerilogRequestLogging();    // 必须在 UseStaticFiles 和 UseRouting 之间
        app.UseRouting();
        app.UseCorsAccessor();
        app.UseSession();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseInject();

        //原生的Context中并没有包含客户端的IP地址跟端口号。
        //我们需要手动的在Nuget中引入
        //"Microsoft.AspNetCore.HttpOverrides": "1.0.0" 这个包
        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });

        app.UseEndpoints(endpoints =>
        {
            // 批量注册集线器
            endpoints.MapHubs();
            endpoints.MapRazorPages();

            endpoints.MapAreaControllerRoute(
                "areas", "areas",
                "{area:exists}/{controller=Home}/{action=Index}/{id?}");

            endpoints.MapControllerRoute(
                "areas",
                "{area:exists}/{controller=Home}/{action=Index}/{id?}"
            );
            endpoints.MapControllerRoute(
                "default",
                "{controller=Home}/{action=Index}/{id?}");
        });

        ApplicationInit.Init();

        Log.Information("测试测试");
    }
}